\ FOOS coffeecup example - Copyright 2012 J.L. Bezemer
\ You can redistribute this file and/or modify it under
\ the terms of the GNU General Public License

\ Inspired by: Bill Venners - JavaWorld, February 1998

include lib/exceptn.4th
include 4pp/lib/foos.4pp

s" Illegal argument" exception value IllegalArgumentExcept
s" Invalid object"   exception value InvalidObjectExcept
s" Invalid cup size" exception value InvalidCupSize

 0  enum SHORT                         \ the different cupsizes
    enum TALL
constant GRANDE

237 constant MAX_SHORT_ML              \ contents of each cupsize
355 constant MAX_TALL_ML
473 constant MAX_GRANDE_ML

:: CoffeeCup
   class
     field:  sizeValid                 \ is the cup a valid cup
     field:  size                      \ the size of the cup
     field:  innerCoffee               \ how much coffee it contains
     method: setSize                   \ setting the cup size
     method: Refilling                 \ refilling the cup
     method: Ordering                  \ ordering the cup
     method: Sipping                   \ sipping from the cup
     method: Spilling                  \ spilling the entire cup
   end-class {
                                       \ convert size to max. content
     :static getMaxAmount {            ( size -- max. contents)
        case
          SHORT  of MAX_SHORT_ML  endof
          TALL   of MAX_TALL_ML   endof
          GRANDE of MAX_GRANDE_ML endof
          IllegalArgumentExcept throw
        endcase
     } ;
                                       \ set the size of the cup
     :method {                         ( size --)
        dup SHORT <> over TALL <> and over GRANDE <> and
        if InvalidCupSize throw then
                                       \ throw if an invalid cup
        true this -> sizeValid ! this -> size !
     } ; defines setSize               \ set the size

     :method {                         ( size startingAmount --)
        over 0< if IllegalArgumentExcept throw then
        over this -> getMaxAmount over <
        if IllegalArgumentExcept throw then
                                       \ throw if it can't fit
        this -> innerCoffee !          \ set the content
        this => setSize                \ set the cup size
     } ; defines Ordering
                                       \ add more coffee to the cup
     :method {                         ( amount -- spillAmount)
        this -> sizeValid @ 0= if InvalidObjectExcept throw then
                                       \ throw if an invalid size
        this -> innerCoffee +!         \ add the coffee
        this -> size @ this -> getMaxAmount
                                       \ check the size
        this -> innerCoffee @ swap 2dup >
        if tuck - swap this -> innerCoffee ! else 2drop 0 then
     } ; defines Refilling             \ check the spill

     :method {                         ( sipSize -- sipSize')
        this -> innerCoffee @ 2dup > if swap then drop
        dup negate this -> innerCoffee +!
     } ; defines Sipping               \ sip from the coffee
                                       \ empty the entire cup
     :method {                         ( --)
        0 this -> innerCoffee dup @ >r ! r>
     } ; defines Spilling

     private{ sizeValid size innerCoffee setSize getMaxAmount }
} ;

static CoffeeCup MyCoffee

SHORT 200 MyCoffee => Ordering
90 MyCoffee => Refilling  . ." ml spilled" cr
50 MyCoffee => Sipping    . ." ml sipped"  cr
25 MyCoffee => Refilling  . ." ml spilled" cr
300 MyCoffee => Sipping   . ." ml sipped"  cr
225 MyCoffee => Refilling . ." ml spilled" cr
MyCoffee => Spilling      . ." ml spilled" cr
